package com.unitd.base.frame.enums;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

/**
 * 枚举类公共对象定义类
 * 用于定义自定义枚举类的一些公共方法,自定义的枚举类需要实现该接口类
 *
 * @author Hudan
 * @version 1.0
 * @filename DisplayedEnum.java
 * @copyright www.unitd.com
 * @date 2020-06-25
 */
public interface DisplayedEnum extends java.io.Serializable {

    Logger logger = LoggerFactory.getLogger(DisplayedEnum.class);
    String DEFAULT_VALUE_NAME = "value";
    String DEFAULT_LABEL_NAME = "label";

    /**
     * 得到枚举属性的实际值
     *
     * @return 实际值
     */
    default String getValue() {
        return findField(DEFAULT_VALUE_NAME);
    }

    /**
     * 得到枚举属性的实际值
     *
     * @return 实际值
     */
    default Integer getIntegerValue() {
        return Integer.valueOf(findField(DEFAULT_VALUE_NAME));
    }

    /**
     * 得到枚举属性的实际值, 重载 接受一个转化函数
     *
     * @return 实际值
     */
    default <T> T getValue(Function<String, T> trans) {
        return trans.apply(findField(DEFAULT_VALUE_NAME));
    }

    /**
     * 得到枚举属性的展示值
     *
     * @return 展示值
     */
    default String getLabel() {
        return findField(DEFAULT_LABEL_NAME);
    }

    /**
     * 根据指定的key查找枚举对象
     *
     * @param fieldName 枚举key
     * @return key对应的枚举值
     */
    default String findField(String fieldName) {
        Field field = ReflectionUtils.findField(this.getClass(), fieldName);
        if (field == null) {
            return null;
        }

        try {
            field.setAccessible(true);
            return field.get(this).toString();
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 返回value->label对
     *
     * @return key-value 的map集合
     */
    static Map<String, String> entries(Class<? extends DisplayedEnum> clazz) throws Exception {
        Object[] enumEntries = clazz.getEnumConstants();
        Objects.requireNonNull(enumEntries, "枚举类不含枚举值！");
        Method labelMethod = clazz.getMethod("get" + StringUtils.capitalize(DEFAULT_LABEL_NAME));
        Method valueMethod = clazz.getMethod("get" + StringUtils.capitalize(DEFAULT_VALUE_NAME));
        Map<String, String> map = new HashMap<>();
        for (Object o : enumEntries) {
            map.put(valueMethod.invoke(o).toString(), labelMethod.invoke(o).toString());
        }
        return map;
    }

    /**
     * 根据值得到相应的枚举对象
     *
     * @param enumType 枚举类对象
     * @param value    枚举值
     * @param <T>      最终得到的枚举对象
     * @return 最终得到的枚举对象
     */
    static <T extends DisplayedEnum> T valueOfEnum(Class<T> enumType, String value) {
        if (value == null) {
            throw new IllegalArgumentException("DisplayedEnum value should not be null");
        }
        if (enumType.isAssignableFrom(DisplayedEnum.class)) {
            throw new IllegalArgumentException("illegal DisplayedEnum type");
        }
        if (!enumType.isEnum()) {
            throw new IllegalArgumentException(enumType.getSimpleName() + " 必须是枚举类型");
        }

        T[] enums = enumType.getEnumConstants();
        for (T t : enums) {
            if (t.getValue().equals(value)) {
                return t;
            }
        }

        logger.warn("枚举类{} value为{}的枚举不存在", enumType.getSimpleName(), value);
        return null;
    }

    /**
     * 得到当前枚举对象实际属性值并显示
     *
     * @return 枚举对象打印
     */
    default String display() {
        return getClass().getSimpleName() + " [" + DEFAULT_VALUE_NAME + " = " + getValue() + ", " + DEFAULT_LABEL_NAME + " = " + getLabel() + "]";
    }
}